package com.xdxc.utils;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;

public final class EncryptionUtils {

    // 禁止实例化
    private EncryptionUtils() {
        throw new AssertionError("Cannot instantiate utility class");
    }

    // 安全参数配置
    private static final String MD5_ALGORITHM = "MD5";
    private static final String SHA_256_ALGORITHM = "SHA-256";
    private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA512"; // 改用更安全的HMAC-SHA512
    private static final int SALT_LENGTH = 32;  // 增加盐长度
    private static final int PBKDF2_ITERATIONS = 150_000;  // 提高迭代次数
    private static final int PBKDF2_KEY_LENGTH = 512;  // 增加密钥长度

    // 安全随机数生成器
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    /**
     * @deprecated MD5已不安全，仅用于兼容旧系统，新系统应使用PBKDF2或SHA-256加盐哈希
     */
    @Deprecated
    public static String md5Encrypt(String input) {
        validateInput(input);
        try {
            MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM);
            return bytesToHex(md.digest(input.getBytes()));
        } catch (NoSuchAlgorithmException e) {
            throw new SecurityException("MD5 algorithm not available", e);
        }
    }

    /**
     * SHA-256哈希（不适用于密码存储，如需存储密码请使用PBKDF2方法）
     */
    public static String sha256Encrypt(String input) {
        validateInput(input);
        try {
            MessageDigest md = MessageDigest.getInstance(SHA_256_ALGORITHM);
            return bytesToHex(md.digest(input.getBytes()));
        } catch (NoSuchAlgorithmException e) {
            throw new SecurityException("SHA-256 algorithm not available", e);
        }
    }

    /**
     * 生成密码学安全的随机盐值
     */
    public static byte[] generateSalt() {
        byte[] salt = new byte[SALT_LENGTH];
        SECURE_RANDOM.nextBytes(salt);
        return salt;
    }

    /**
     * PBKDF2密码哈希（推荐用于密码存储）
     *
     * @param password 明文密码（会自动清除内存中的内容）
     * @param salt     盐值（应存储到数据库）
     * @return Base64编码的哈希值
     */
    public static String pbkdf2Hash(char[] password, byte[] salt) {
        validatePassword(password);
        validateSalt(salt);

        try {
            PBEKeySpec spec = new PBEKeySpec(
                    password,
                    salt,
                    PBKDF2_ITERATIONS,
                    PBKDF2_KEY_LENGTH
            );

            SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
            byte[] hash = skf.generateSecret(spec).getEncoded();
            spec.clearPassword();  // 清除敏感数据

            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception e) {
            throw new SecurityException("PBKDF2 hashing failed", e);
        }
    }

    /**
     * 安全验证密码（恒定时间比较）
     *
     * @param attemptedPassword 尝试的密码（会自动清除内存中的内容）
     * @param storedHash        存储的哈希值
     * @param salt              对应的盐值
     */
    public static boolean verifyPbkdf2Hash(char[] attemptedPassword, String storedHash, byte[] salt) {
        validatePassword(attemptedPassword);
        validateSalt(salt);

        String hashedAttempt = pbkdf2Hash(attemptedPassword, salt);
        Arrays.fill(attemptedPassword, '\0');  // 立即清除密码内容

        return secureStringCompare(hashedAttempt, storedHash);
    }

    /**
     * 安全字符串比较（恒定时间算法）
     */
    private static boolean secureStringCompare(String a, String b) {
        byte[] aBytes = a.getBytes();
        byte[] bBytes = b.getBytes();

        int diff = aBytes.length ^ bBytes.length;
        for (int i = 0; i < aBytes.length && i < bBytes.length; i++) {
            diff |= aBytes[i] ^ bBytes[i];
        }
        return diff == 0;
    }

    // 辅助方法
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    // 输入验证
    private static void validateInput(String input) {
        if (input == null || input.isEmpty()) {
            throw new IllegalArgumentException("Input cannot be null or empty");
        }
    }

    private static void validatePassword(char[] password) {
        if (password == null || password.length == 0) {
            throw new IllegalArgumentException("Password cannot be null or empty");
        }
    }

    private static void validateSalt(byte[] salt) {
        if (salt == null || salt.length != SALT_LENGTH) {
            throw new IllegalArgumentException("Salt must be " + SALT_LENGTH + " bytes");
        }
    }

    // 兼容旧系统的包装方法（不建议使用）
    public static String pbkdf2Hash(String password, byte[] salt) {
        char[] passwordChars = password.toCharArray();
        String hash = pbkdf2Hash(passwordChars, salt);
        Arrays.fill(passwordChars, '\0');
        return hash;
    }

    public static boolean verifyPbkdf2Hash(String attemptedPassword, String storedHash, byte[] salt) {
        char[] passwordChars = attemptedPassword.toCharArray();
        boolean result = verifyPbkdf2Hash(passwordChars, storedHash, salt);
        Arrays.fill(passwordChars, '\0');
        return result;
    }}